#include <krtpch.h>

#include "SceneLoader.h"

#include <ctime>

#include <stb_image.h>
#include <stb_image_write.h>

std::string GetFileName(const krt::Scene& scene);

krt::Ref<krt::Scene> InitScene();

int main()
{
	auto scene = InitScene();

	krt::KRTMath::Srand48((unsigned int)std::time(NULL));

	auto imageBuffer = krt::MakeRef<krt::ImageBuffer>(scene->GetSize().x, scene->GetSize().y);

	int samplerNumbers = 60, depth = 4;
	
	auto rayTracer = krt::Singleton<krt::RayTracer>::GetInstance(scene, samplerNumbers, depth);

	auto startTime = std::clock();

	auto& state = rayTracer->GetState();

	rayTracer->Start(*imageBuffer, false, scene->GetSize().y / state.threadNum / 2);

	int total_progress = 0;
	for (auto& progress : state.progress) {
		total_progress += progress.total;
	}

	int curProgress;
	while (1) {
		curProgress = 0;
		for (auto& progress : state.progress) {
			curProgress += progress.current;
		}
		printf("Progress: %d/%d, (%f%%)                ",
			curProgress, total_progress, (float)curProgress / (float)total_progress * 100.0f);

		if (curProgress >= total_progress) {
			printf("\n");
			break;
		}
		printf("\r");
	}

	rayTracer->Shutdown();

	printf("Time: %fs\n", (float)(clock() - startTime) * 0.001f);

	// output
	auto fileStr = GetFileName(*scene);
	std::cout << "Output File Path: " << fileStr << std::endl;
	stbi_flip_vertically_on_write(true);
	auto wirteRes = stbi_write_jpg(fileStr.c_str(),
		imageBuffer->GetWidth(), imageBuffer->GetHeight(), imageBuffer->GetChannel(), *imageBuffer, 0);

	std::cout << "Output Result: " << (wirteRes ? "Successed" : "Failed") << std::endl;

#ifdef KRT_PLATFORM_WINDOWS
	system(("start " + fileStr).c_str());
#endif // KRT_PLATFORM_WINDOWS


	return 0;
}

krt::Ref<krt::Scene> InitScene() {
	// init
	auto outPutSize = glm::ivec2(800, 800);
	
	// camera
	glm::vec3 ca_org = { 0.0f,0.0f,-6.0f };
	glm::vec3 ca_fl = { 0.0f,0.0f,-1.0f };
	auto camera = krt::MakeRef<krt::ProjectionCamera>(krt::ProjectionCamera(
		ca_org, ca_fl, 70.0f, (float)outPutSize.x / (float)outPutSize.y, 0.0f, glm::distance(ca_org, ca_fl)
	));
	
	// metarial
	auto lamberT1 = krt::MakeRef<krt::Lambertian>(
		krt::MakeRef<krt::ConstantTexture>(krt::ConstantTexture({ 1.0f, 0.0f, 0.0f }))
		);
	auto lamberT2 = krt::MakeRef<krt::Lambertian>(
		krt::MakeRef<krt::ConstantTexture>(krt::ConstantTexture({ 0.f, 1.0f, 0.0f }))
		);
	auto lamberT3 = krt::MakeRef<krt::Lambertian>(
		krt::MakeRef<krt::ConstantTexture>(krt::ConstantTexture({ 0.0f, 0.0f, 1.0f }))
		);
	auto metalT = krt::MakeRef<krt::Metal>(
		krt::MakeRef<krt::ConstantTexture>(krt::ConstantTexture({ 1.0f, 1.0f, 1.0f }))
		);
	auto lightT = krt::MakeRef<krt::Illumination>(krt::Illumination(nullptr, { 1.0f,1.0f,1.0f }));

	// Accel
	auto objects = krt::MakeRef<krt::ListAccel>();

	// Primitive
	auto trans1 = krt::Transform::Create({ 0.0f,0.0f,-1.0f }, glm::vec3(0.5f));
	auto trans1in = krt::Transform::Inverse(trans1);
	auto sp1 = krt::MakeRef<krt::GeometryPrimitive>(lamberT1, krt::MakeRef<krt::SdfSphere>(trans1, trans1in, false));
	objects->AddHitable(sp1);

	auto trans2 = krt::Transform::Create({ 1.0f,0.0f,-1.0f }, glm::vec3(0.6f));
	auto trans2in = krt::Transform::Inverse(trans2);
	auto sp2 = krt::MakeRef<krt::GeometryPrimitive>(lamberT2, krt::MakeRef<krt::SdfSphere>(trans2, trans2in, false));
	objects->AddHitable(sp2);

	auto trans3 = krt::Transform::Create({ 0.0f,-100.5f, -1.0f }, glm::vec3(100.0f));
	auto trans3in = krt::Transform::Inverse(trans3);
	auto sp_gd = krt::MakeRef<krt::GeometryPrimitive>(lamberT3, krt::MakeRef<krt::SdfSphere>(trans3, trans3in, false));
	objects->AddHitable(sp_gd);

	//auto trans4 = krt::Transform::Create({ 0.5f,2.0f,-1.0f }, glm::vec3(1.0f));
	//auto trans4in = krt::Transform::Inverse(*trans4);
	//auto sp_li = krt::MakeRef<krt::GeometryPrimitive>(lightT, krt::MakeRef<krt::SdfSphere>(trans4, trans4in, false));
	//objects->AddHitable(sp_li);

	// Scene
	auto scene = krt::MakeRef<krt::Scene>(krt::Scene("Test", outPutSize, camera, objects));
	
	scene->GetSkybox()->SetKeyFunc([](const glm::vec3& normal)->glm::vec3 {
		float t = 0.5f * (normal.y + 1.0f);
		return { (1.0f - t) * glm::vec3(1.0f,0.9f,0.8f) + t * glm::vec3(0.5f,0.7f,1.0f) };
		});

	return scene;
}

std::string GetFileName(const krt::Scene& scene)
{
	char timeCh[64] = { '\0' };
	time_t rawTime;
	std::time(&rawTime);
	std::strftime(timeCh, 64, "%Y.%m.%d-%H.%M.%S", std::localtime(&rawTime));
	std::string timeStr(timeCh);
	std::string appType;

#if defined KRT_DEBUG
	appType = "Debug";
#elif defined KRT_RELEASE
	appType = "Release";
#endif

	return "ImgOut/" + timeStr + "_" + appType + "_" + scene.GetName() + ".jpg";
}